package com.epam.concurrency.circbuffer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;

/**
 * Standard Circular Buffer
 * @author Yuriy Miedviediev
 * @version 1.0 Build 15.05.2014
 */
public class CircularBuffer {

    private static final Logger LOGGER = LoggerFactory.getLogger(CircularBuffer.class);
    private static final int DEFAULTCAPACITY = 10;

    //capacity of the buffer
    private final int capacity;

    //data holder
    private int[] contents;

    //current indexes for adding and deleting elements
    private int addIndex = 0;
    private int takeIndex = 0;

    //number of elements in the buffer
    private int length = 0;

    public CircularBuffer(int capacity) {
        this.capacity = capacity;
        contents = new int[capacity];
    }

    public CircularBuffer() {
        this(DEFAULTCAPACITY);
    }

    public int[] getContents() {
        return Arrays.copyOf(this.contents, this.contents.length);
    }

    /**
     * Add new element to the buffer
     * @param element integer to add to the buffer
     */
    public synchronized void add(int element) {

        while(this.isFull())  {
            try {
                wait();
            } catch (InterruptedException e) {
                LOGGER.error(e.getMessage());
            }
        }

        this.addIndex = this.addIndex % this.capacity;
        contents[this.addIndex] = element;
        this.addIndex++;
        this.length++;
        if(this.length > this.capacity) {
            this.length = this.capacity;
        }

        notifyAll();
    }

    /**
     * Take and delete (move deletion cursor) element from the buffer
     * @return next integer in the queue
     */
    public synchronized int poll() {

        while(this.length <= 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                LOGGER.error(e.getMessage());
            }
        }

        this.takeIndex = takeIndex % this.capacity;
        int result = this.contents[takeIndex];
        this.takeIndex++;
        this.length--;
        if(this.length < 0) {
            this.length = 0;
        }
        notifyAll();
        return result;
    }

    /**
     * Size of the Circular Buffer
     * @return number of elements in the buffer
     */
    public int size() {
        return this.length;
    }

    /**
     * Check if the buffer is full
     * @return true, if buffer is full, false otherwise
     */
    public synchronized boolean isFull() {
        return this.length >= this.capacity;
    }
}
